/*
 * TMap Designer - A Thematic Map design software
 * Copyright (C) 2006-2009 World Health Organization
 * http://www.who.int
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
package who.tmap.ui;

import java.awt.Color;
import java.awt.Container;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JToggleButton;
import javax.swing.SwingUtilities;
import javax.swing.event.MouseInputAdapter;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;

import org.apache.log4j.Logger;
import org.bushe.swing.event.annotation.AnnotationProcessor;
import org.bushe.swing.event.annotation.EventSubscriber;

import who.tmap.common.TMapException;
import who.tmap.digitizer.DigitizerEntry;
import who.tmap.digitizer.DigitizerService;
import who.tmap.digitizer.impl.DigitizerServiceImpl;
import who.tmap.toolkit.MappingToolKit;
import who.tmap.toolkit.impl.AttrHelper;
import who.tmap.toolkit.shape.LabelShape;
import who.tmap.toolkit.shape.MapShape;
import who.tmap.toolkit.shape.MapShapes;
import who.tmap.toolkit.shape.PolygonShape;
import who.tmap.toolkit.shape.annotation.CrossShape;
import who.tmap.toolkit.shape.annotation.EllipseShape;
import who.tmap.toolkit.shape.annotation.ImageShape;
import who.tmap.toolkit.shape.annotation.LabelsListShape;
import who.tmap.toolkit.shape.annotation.LegendShape;
import who.tmap.toolkit.shape.annotation.LineShape;
import who.tmap.toolkit.shape.annotation.RectangleShape;
import who.tmap.toolkit.shape.annotation.TextShape;
import who.tmap.toolkit.shape.annotation.TitleShape;
import who.tmap.toolkit.shape.annotation.TriangleShape;
import who.tmap.toolkit.shape.cdpointsymbol.CDPointSymbol;
import who.tmap.ui.cd.point.JPanelCDPoint;
import who.tmap.ui.event.DigitizerInput;
import who.tmap.ui.undo.PolygonShapeUndoInfo;
import who.tmap.ui.undo.UndoActions;
import who.tmap.ui.undo.UndoManager;
import who.tmap.ui.undo.impl.PolygonShapeUndoInfoImpl;
import who.tmap.ui.undo.impl.UndoManagerImpl;
import who.tmap.ui.util.CursorHelper;
import who.tmap.ui.util.JDialogFontChooser;
import who.tmap.ui.util.MiscUIHelper;

/**
 * Takes action based on mouse input (click, move, drag, etc...) on the map
 * @author stl
 *
 */
public class UserMouseInputListenerImpl extends MouseInputAdapter 
										implements UserMouseInputListener {

    private static Logger logger = Logger.getLogger(UserMouseInputListenerImpl.class); 
    
    private static final String COORDS_FORMAT ="Lat, Lon=({0,number,#.###},{1,number,#.###}) x, y=({2,number,#},{3,number,#})";
    
    private MappingToolKit mtk;
    private Container container;
    private TableModel dataTableModel;
    private JPanelCDPoint jPanelCDPoint;
    
    private boolean selectOp =true;
    private boolean multiSelectOp =false;
    private boolean polygonOp =false;
    private boolean splitPolygonOp =false;
    private boolean insertPolygonPointOp =false;
    private boolean deletePolygonPointOp =false;
    private boolean useExistingBoundariesOp =false;
    private boolean lineOp =false;
    private boolean ellipseOp =false;
    private boolean rectangleOp =false;
    private boolean crossOp =false;
    private boolean triangleOp =false;
    private boolean imageOp =false;
    private boolean fillOp =false;
    private boolean textOp =false;
    private boolean labelListOp =false;
    private boolean moveOp =false;
    private boolean disputedBorderOp =false;
    private boolean zoomInOp =false;
    private boolean zoomOutOp =false;
    private boolean pointEntryOp =false;
    private boolean pointEntryWithoutDescOp =false;    
    private boolean digitizerWithoutDescOp =false;
    private boolean digitizerOp =false;
    
    private int editMode;
    
    private String pointEntryXColumn;
    private int pointEntryXColumnIdx =-1;
    private String pointEntryYColumn;
    private int pointEntryYColumnIdx =-1;    
    
    private JToggleButton jButtonPolygon =null;
    private JToggleButton jButtonSelect =null; 
    private JLabel jLabelCoords =null;
    private JLabel jLabelZoomInfo = null;
    
    // Use existing boundaries constants
    private static final int UEB_SELECT_SHAPE =0;
    private static final int UEB_SELECT_FIRST_POINT =1;
    private static final int UEB_SELECT_SECOND_POINT =2;
    private static final int UEB_SELECT_PATH =3;
    
    private int uebState =UEB_SELECT_SHAPE;
    private MapShape uebShape =null;
    private Point2D uebFirstPt =null, uebSecondPt =null, uebPathPt =null, uebAnchorPt =null;
    private Point2D mousePressedPt =new Point2D.Double();
    private PolygonShapeUndoInfo mousePressedUndoInfo;
    
    // Split polygon constants
    private static final int SP_SELECT_FIRST_POINT =0;
    private static final int SP_SELECT_SECOND_POINT =1;
    
    private int spState =SP_SELECT_FIRST_POINT;
    private Point2D spFirstPt =null, spSecondPt =null;
    private PolygonShape spPolygon =null;
    
    private MiscUIHelper miscUIHelper =new MiscUIHelper();
    private CursorHelper cursorHelper =new CursorHelper();
    private AttrHelper attrHelper =new AttrHelper();
    
    private DigitizerService digitizerService =DigitizerServiceImpl.getInstance();
    private UndoManager undoManager =UndoManagerImpl.getInstance();
    
    private BufferedImage thematicMapCaptureTest =null;
       
    public UserMouseInputListenerImpl() {
        super();
        
        AnnotationProcessor.process(this);
    }
    
    public void setEditMode( int editMode ) {
    	this.editMode =editMode;
    	
    	mtk.setEditMode( editMode );
    }
    
    private void setDirty() {
	    mtk.getThematicMap().setDirty( true );    	
    }
       
	public void mouseClicked(MouseEvent e) { 
		
		if ( selectOp ) {
		    mouseClickedSelectOp( e );	
								
		} else if ( polygonOp ) {
		    mouseClickedPolygonOp( e );		    
			
		} else if ( fillOp ) {
		    mouseClickedFillOp( e );
								
		} else if ( textOp ) {
		    mouseClickedTextOp( e );
		    
		} else if ( labelListOp ) {
		    mouseClickedLabelListOp( e );		    
	
		} else if ( imageOp ) {
		    mouseClickedImageOp( e );
		
		} else if ( insertPolygonPointOp ) {
		    mouseClickedInsertPolygonPointOp( e );		    
		    
		} else if ( deletePolygonPointOp ) {
		    mouseClickedDeletePolygonPointOp( e );
		    
		} else if ( useExistingBoundariesOp ) {
		    mouseClickedUseExistingBoundariesOp( e );
		    
		} else if ( splitPolygonOp ) {
		    mouseClickedSplitPolygonOp( e );
		    
		} else if ( disputedBorderOp  ) {
			mouseClickedDisputedBorderOp( e );
		
		} else if ( pointEntryOp ) {
			mouseClickedPointEntryOp( e );
			
		} else if ( pointEntryWithoutDescOp ) {			
			mouseClickedPointEntryWithoutDescOp( e );
			
		} else if ( zoomInOp  ) {
			mouseClickedZoomInOp( e );
			
		} else if ( zoomOutOp ) {
			mouseClickedZoomOutOp( e );			
		}
		
	}
	
	private boolean startDigitizer() {
		
		boolean ret =false;
		
		if ( digitizerService.relativeCoordinatesSystemIsConfigured() ) {
	    	try {
	    		mtk.computeDigitizerFactors( digitizerService.getXMin(), digitizerService.getYMin(), 
	    									 digitizerService.getXMax(), digitizerService.getYMax() );
	    		
	    		digitizerService.start();
	    		
				ret =true;
				
			} catch (Exception e) {
				StringBuilder message =new StringBuilder( "<HTML>" )
											.append( "Unable to establish a connection with your device.<BR>" )
											.append( "Please, check your device configuration:<BR>" )
											.append( "<UL>" )
											.append( "<LI>Right click on the Thematic Map icon" )
											.append( "<LI>Choose \"Properties...\"" )
											.append( "<LI>Go to the \"Data Aquisition\" tab")
											.append( "<LI>Choose the \"Device Configuration\" tab")
											.append( "</UL>" )
											.append( "</HTML>" );
				
				JOptionPane.showMessageDialog( container, message.toString(), "Unable to connect", JOptionPane.ERROR_MESSAGE );
			}
			
		} else {
			StringBuilder message =new StringBuilder( "<HTML>" )
										.append( "Your relative coordiantes system is not correctly set.<BR>" )
										.append( "Please, check your configuration:<BR>" )
										.append( "<LI>Right click on the Thematic Map icon" )
										.append( "<LI>Choose \"Properties...\"" )
										.append( "<LI>Go to the \"Data Aquisition\" tab")
										.append( "<LI>Choose the \"Coordinates System\" tab")
										.append( "</UL>" )
										.append( "</HTML>" );
			
			JOptionPane.showMessageDialog( container, message.toString(), "Undefined coordinates system", JOptionPane.ERROR_MESSAGE);
		}
		
		return ret;
	}
	
	/**
	 * TMap interaction method with the data acquisition source ( digitizer tablet )
	 */
	public void processDigitizerEntry( DigitizerEntry digitizerEntry ) {
		
		Point2D digiPoint =new Point2D.Double( digitizerEntry.getX(), digitizerEntry.getY() );
		Point2D point =mtk.convertDigitizerToMap( digiPoint );
		
		logger.debug( "Current point = ["+ point + "], action=[" + digitizerEntry.getAction() + "]" );
			
		if ( mtk.getAnchor() == null ) {
			
			if ( digitizerEntry.getAction() == DigitizerEntry.NEW_POINT_ACTION ) {
				mtk.unselectShapes();
				container.repaint();
				
				try {
					PolygonShape shape =(PolygonShape)mtk.addShape( MapShapes.POLYGON );
					shape.addPoint( point );
					
				    undoManager.push( UndoActions.POLYGON, shape );

				    setDirty();
				} catch (TMapException e1) {
					logger.error( e1 );
				}
				
				mtk.setAnchor( point );
				mtk.setDrag( point );
			}

		} else {
			 if ( digitizerEntry.getAction() == DigitizerEntry.NEW_POINT_ACTION ) {

				((PolygonShape)mtk.getEditedShape()).addPoint( point );				
				mtk.setAnchor( point );
				mtk.setDrag( point );
				container.repaint();
				
			// Close polygon
			} else if ( digitizerEntry.getAction() == DigitizerEntry.CLOSE_POLYGON_ACTION ) { 
				MapShape polygon =mtk.getEditedShape();
				mtk.closePolygon();
				container.repaint();
				
				mtk.setAnchor( null );
				mtk.setDrag( null );	
				
				if ( digitizerOp ) {
					String desc =JOptionPane.showInputDialog( null, "Please enter a description for this polygon"
							, "Digitizer input", JOptionPane.QUESTION_MESSAGE );
					
					if ( desc != null ) {
						((PolygonShape)polygon).setName( desc );
					}
				}
				
			    /*jButtonSelect.doClick();
			    mtk.selectShape( polygon );
				mtk.setEditedShape( polygon );*/
			}
			 
			setDirty();
		}		
	}
	
	private void resetSplitPolygonValues() {
	    spState =SP_SELECT_FIRST_POINT;
	    spFirstPt =null;
	    spSecondPt =null;
	    spPolygon =null;
	    mtk.setAnchor( null );
	    mtk.setDrag( null );
	    
	    setCursor( CursorHelper.Cursors.DEFAULT );
	}
	
	public void cancelCurrentEditing() {
		mtk.unselectShapes();
		
		jButtonSelect.doClick();
	}
	
	private void mouseClickedSplitPolygonOp(MouseEvent e) {
		MapShape shape =mtk.getHandleAt( getScaledPoint( e.getPoint() ) );
		if ( shape != null ) {
	        PolygonShape polygon =(PolygonShape)shape;
		    int handleIdx =polygon.getSelectedHandleIndex();
		    Point2D pt =polygon.getExactPointAtHandle( handleIdx );
		    if ( spState == SP_SELECT_FIRST_POINT ) {
		        spFirstPt =pt;
		        spPolygon =polygon;
		        spState =SP_SELECT_SECOND_POINT;
			    mtk.setAnchor( e.getPoint() );
			    			    
		    } else if ( spState == SP_SELECT_SECOND_POINT ) {
		        if ( spPolygon == polygon ) {
		            spSecondPt =pt;
		            try {
		            	PolygonShape ps =(PolygonShape)polygon.deepClone();
		            	
                        List<MapShape> polygons =mtk.splitPolygon( spPolygon, spFirstPt, spSecondPt );
                        polygons.add( ps );
                        undoManager.push( UndoActions.SPLIT_POLYGON, polygons );
                        
                        container.repaint();
                    } catch (Exception e1) {
                        miscUIHelper.displayErrorMessage( container, e1 );
                    }
                    
                    jButtonSelect.doClick();
    	    		resetSplitPolygonValues();	                    
		        }
		    } else {
		    	miscUIHelper.displayErrorMessage( container, "Invalid split polygon state [" + spState + "]" );
	    		resetSplitPolygonValues();		
		    }
		    
		} else {
    		resetSplitPolygonValues();				
			jButtonSelect.doClick();
		}

	}
	
	private void resetUseExistingBoundariesValues() {
	    uebState =UEB_SELECT_SHAPE;
	    uebShape =null;
	    uebFirstPt =null;
	    uebSecondPt =null;	
	    uebPathPt =null;
	    uebAnchorPt =null;
	    
	    setCursor( CursorHelper.Cursors.DEFAULT );
	}
	
	private void mouseClickedDisputedBorderOp( MouseEvent e ) {
	    mtk.addDisputedBorder( getScaledPoint( e.getPoint() )  );
	    
	    undoManager.push( UndoActions.DISPUTED_BORDER, getScaledPoint( e.getPoint() ) );
	    
	    setDirty();
	    container.repaint();
	}
	
	private void mouseClickedPointEntryOp( MouseEvent e ) {
		String desc =JOptionPane.showInputDialog( null, "Please enter a description for this point"
												, "Point entry", JOptionPane.QUESTION_MESSAGE );		
		
		addPointEntry( e, desc );
	}
	
	private void mouseClickedPointEntryWithoutDescOp( MouseEvent e ) {
		addPointEntry( e, null );
	}
	
	@SuppressWarnings("unchecked")
	private void addPointEntry( MouseEvent e, String desc ) {
		Point2D pt =getScaledPoint( e.getPoint() );
		List row =mtk.addPointEntry( pt, desc );
		
		undoManager.push( UndoActions.ADD_CD_POINT, row );
		
		setDirty();
		container.repaint();
		((AbstractTableModel)dataTableModel).fireTableDataChanged();
		jPanelCDPoint.refreshEntryTables();
	}

	private void mouseClickedUseExistingBoundariesOp(MouseEvent e) {
	    logger.debug( "Entering mouseClickedUseExistingBoundariesOp state=" + uebState );
	    
	    switch( uebState ) {
	    	    
	    	case UEB_SELECT_SHAPE:
	    	    resetUseExistingBoundariesValues();	    	    
				uebShape =mtk.selectShapeAt( getScaledPoint( e.getPoint() ) );
				if ( uebShape != null ) {
				    uebState =UEB_SELECT_FIRST_POINT;
				    setCursor( CursorHelper.Cursors.MERGE_CB1 );

				    container.repaint();
				} else {
				    resetUseExistingBoundariesValues();
				}
				break;
				
			case UEB_SELECT_FIRST_POINT:
			case UEB_SELECT_SECOND_POINT:
				MapShape shape =mtk.getHandleAt( getScaledPoint( e.getPoint() ) );
				if ( shape == uebShape ) {
			        PolygonShape polygon =(PolygonShape)shape;
				    int handleIdx =polygon.getSelectedHandleIndex();
				    Point2D exactPt =polygon.getExactPointAtHandle( handleIdx );
				    
				    if ( uebState == UEB_SELECT_FIRST_POINT ) {
				      uebFirstPt =exactPt;
				      uebState =UEB_SELECT_SECOND_POINT;
				      
				      ((PolygonShape)mtk.getEditedShape()).addPoint( uebFirstPt );

					  setCursor( CursorHelper.Cursors.MERGE_CB2 );
				      
				      container.repaint();
				    
				    } else {
					  uebSecondPt =exactPt;
					  uebAnchorPt =mtk.convertMapToScreen( exactPt );
					  uebState =UEB_SELECT_PATH;				        
					  
					  setCursor( CursorHelper.Cursors.MERGE_CB3 );

				    }
				} else {
				    resetUseExistingBoundariesValues();
				}
				break;
				
			case UEB_SELECT_PATH:
			    uebPathPt =getScaledPoint( e.getPoint() );
			    try {
                    ((PolygonShape)mtk.getEditedShape()).addCommonBoundary( (PolygonShape)uebShape, uebFirstPt, uebSecondPt, uebPathPt );
                } catch (TMapException e1) {
                    miscUIHelper.displayErrorMessage( container, e1 );
                }

                logger.debug( "uebAnchor=" + uebAnchorPt );
			    mtk.setAnchor( (Point2D)uebAnchorPt.clone() );
			    mtk.setDrag( (Point2D)uebAnchorPt.clone() );

			    setDirty();			    
			    
			    jButtonPolygon.doClick();
			     
			    resetUseExistingBoundariesValues();			    
			    break;
			    
			default:
				miscUIHelper.displayErrorMessage( container, "Invalid use existing boundaries state [" + uebState + "]" );
		    	resetUseExistingBoundariesValues();
	    }
	    
	    logger.debug( "Leaving mouseClickedUseExistingBoundariesOp state=" + uebState );	
		    
	}
		
	public void mousePressed( MouseEvent e ) {
		
		// For move undo
		mousePressedPt.setLocation( e.getPoint() );
		
		if ( selectOp && mtk.anyShapeSelected() ) {
			Point2D pt =getScaledPoint( e.getPoint() );
			
			// mouse pressed over a handle
			// sometimes handles are bigger than the shape itself, which causes trouble for moving the shape...
			MapShape shape =mtk.getHandleAt( pt );
			if ( shape != null ) {

			    mtk.setAnchor( e.getPoint() );
			    if ( shape instanceof PolygonShape ) {
			        PolygonShape polygon =(PolygonShape)shape;
				    int handleIdx =polygon.getSelectedHandleIndex();
				    Point2D exactPt =polygon.getExactPointAtHandle( handleIdx );
				    mtk.setAnchor( exactPt );
				    
				    // For undo purpose
				    mousePressedUndoInfo =new PolygonShapeUndoInfoImpl();
				    mousePressedUndoInfo.cloneInfoFromPolygonShape( polygon );
			    }
				mtk.setEditedShape( shape );
				logger.debug( "handle " + shape.getSelectedHandleIndex() + " clicked !" );
				
			// mouse pressed over a shape (but not over one of its handles)
			} else {
				boolean valid =false;
				
				mtk.captureThematicMapBackground();
				mtk.captureSelectedShapes();
				mtk.setRepaintNeeded( false );
				
				// make sure the click was done upon a selected shape
				for( MapShape mapShape : mtk.getSelectedShapes() ) {
					if ( mapShape.contains( pt ) || mapShape.getHandleIndexAt( pt ) != -1 ) {
						valid =true;
						break;
					}
				}
				
				if ( valid ) {
				    mtk.setAnchor( e.getPoint() );
				    mtk.setDrag( e.getPoint() );
				    setCursor( CursorHelper.Cursors.MOVE );
				    
				    //logger.debug( "mousePressedPt=" + mousePressedPt.getX() + "," + mousePressedPt.getY() );
				} else {
					if ( !e.isShiftDown() ) {
						mtk.unselectShapes();
					}
				}
			}
			
		} else if ( multiSelectOp ) {
			
			if ( mtk.getAnchor() == null ) {				
				mtk.unselectShapes();
				
				container.repaint();
				
				mtk.setAnchor( e.getPoint() );
				mtk.setDrag( mtk.getAnchor() );
			}
			
		} else {
			performOp( e );
		}

	}
	
	private void performOp( MouseEvent e ) {
		
		if ( lineOp ) {
			mouseClickedLineOp( e );
			
		} else if ( rectangleOp ) {
		    mouseClickedRectangleOp( e );	
		    
		} else if ( triangleOp ) {
		    mouseClickedTriangleOp( e );			    
		    
		} else if ( crossOp ) {
		    mouseClickedCrossOp( e );		    
		    
		} else if ( ellipseOp ) {
		    mouseClickedEllipseOp( e );	
		    
		}
		
	}
	
	public void mouseReleased(MouseEvent e) {
		
		mtk.setRepaintNeeded( true );		
		
		if ( selectOp && mtk.anyShapeSelected() && mtk.getEditedShape() != null && mtk.getAnchor() != null ) {	
		    Point2D pt =getScaledPoint( e.getPoint() );
		    
		    List<PolygonShapeUndoInfo> undoInfos =new ArrayList<PolygonShapeUndoInfo>();
		    undoInfos.add( mousePressedUndoInfo );
		    
		    // If a shape was updated (not moved)
		    if (  mtk.getEditedShape().getSelectedHandleIndex() != -1  ) {
				    
		    	// It's a polygon point
		    	if ( mtk.getEditedShape() instanceof PolygonShape ) {
				    // Move points with identical coordinates in other shapes
			    	for( MapShape aShape : mtk.getShapes() ) {
				        if ( aShape instanceof PolygonShape && ((PolygonShape)aShape).containsEdge( mtk.getAnchor() ) ) {
				        	PolygonShapeUndoInfo undoInfo =new PolygonShapeUndoInfoImpl();
				        	undoInfo.cloneInfoFromPolygonShape( ((PolygonShape)aShape) );
				        	undoInfos.add( undoInfo );
				        	
							((PolygonShape)aShape).movePoint( mtk.getAnchor(), pt );
				        }
				    }
			    	
					undoManager.push( UndoActions.MOVE_POLYGON_POINT, undoInfos );

				// It's an annotation point
		    	} else {
		    		undoManager.push( UndoActions.MOVE_POINT, mtk.getEditedShape(), getScaledPoint( mousePressedPt )
		    																	  , getScaledPoint( e.getPoint() )
		    																	  , mtk.getEditedShape().getSelectedHandleIndex() );
		    	}
		    	
		    	mtk.getEditedShape().setSelectedHandleIndex( -1 );

		    // If a shape was moved
		    } else {
		    	List<MapShape> shapes =new ArrayList<MapShape>( mtk.getSelectedShapes() );

		    	undoManager.push( UndoActions.MOVE, shapes, (Point2D)mousePressedPt.clone()
		    											  , (Point2D)e.getPoint().clone() );
		    	
		    	//logger.debug( "dragPt =" + e.getPoint().getX() + ", " + e.getPoint().getY() );	    	
		    }
		    	
		    setCursor( CursorHelper.Cursors.DEFAULT );
		    
		    mtk.setAnchor(null);
		    mtk.setDrag(null);			
			
			container.repaint();
			
			((AbstractTableModel)dataTableModel).fireTableDataChanged();			

		} else if ( multiSelectOp ) {
		    mtk.selectShapesAt( getScaledPoint( mtk.getAnchor() ), getScaledPoint( e.getPoint() ) );
			
			container.repaint();
			
			mtk.setAnchor( null );
			mtk.setDrag( null );	
			
			jButtonSelect.doClick();
			
		} else {
			performOp( e );
			
		}
		
	} 	
	
	/**
	 * Substract scrollbars coordinates from a screen point
	 * @param pt
	 * @return
	 */
	private Point2D applyViewPosition( Point2D pt ) {
	    Point2D viewPos =((JScrollPane)container).getViewport().getViewPosition();
	    
	    return new Point2D.Double( pt.getX() - viewPos.getX(), pt.getY() - viewPos.getY() );
	}
	
	public void mouseMoved(MouseEvent e) {

		if ( ( polygonOp && mtk.getAnchor() != null )
		        || ( useExistingBoundariesOp && uebState == UEB_SELECT_FIRST_POINT )
		        || ( splitPolygonOp && spState == SP_SELECT_SECOND_POINT )
		    ) {
		    Point2D anchor =applyViewPosition( mtk.getAnchor() );
		    Point2D drag =applyViewPosition( mtk.getDrag() != null ? mtk.getDrag() : mtk.getAnchor() );
		    Point2D newDrag =applyViewPosition( e.getPoint() );		    
		    
			mtk.xorLine( container.getGraphics(), anchor, drag );
			mtk.setDrag( e.getPoint() );
			mtk.xorLine( container.getGraphics(), anchor, newDrag );	
			
		} else if ( zoomInOp && mtk.getAnchor() != null && mtk.getDrag() != null ) {
		    Point2D anchor =applyViewPosition( mtk.getAnchor() );
		    Point2D drag =applyViewPosition( mtk.getDrag() );
		    Point2D newDrag =applyViewPosition( e.getPoint() );		
		    
			mtk.xorRectangle( container.getGraphics(), anchor, drag );
			mtk.setDrag( e.getPoint() );	
			mtk.xorRectangle( container.getGraphics(), anchor, newDrag );			
			
		} 
	
		displayCoords( e.getPoint() );
	}
	
	private void displayCoords( Point pt ) {
		Point2D coords =mtk.convertScreenToMap( pt );
		jLabelCoords.setText( MessageFormat.format( COORDS_FORMAT,  
							  new Object[] { -coords.getY(), coords.getX(), pt.x, pt.y } ) );		
	}

	public void mouseDragged(MouseEvent e) {
		
		if ( selectOp && mtk.getEditedShape() != null ) {
			// if clicked on a handle, update the shape
			if ( mtk.getEditedShape().getSelectedHandleIndex() != -1 ) {
				mtk.getEditedShape().updateShape( mtk.getEditedShape().getSelectedHandleIndex()
												, getScaledPoint( e.getPoint() ) );
				
				// STL 2009.01.05
				// to be optimized later using paintSelectedShapes
				container.repaint();
				
			// otherwise move it
			} else {
				
				mtk.moveSelectedShapes( e.getPoint() );
				
				mtk.paintSelectedShapes( (Graphics2D)container.getGraphics() );
	
			}
			setDirty();

		} else if ( multiSelectOp && mtk.getAnchor() != null && mtk.getDrag() != null ) {
		    Point2D anchor =applyViewPosition( mtk.getAnchor() );
		    Point2D drag =applyViewPosition( mtk.getDrag() );
		    Point2D newDrag =applyViewPosition( e.getPoint() );		
		    
			mtk.xorRectangle( container.getGraphics(), anchor, drag );
			mtk.setDrag( e.getPoint() );	
			mtk.xorRectangle( container.getGraphics(), anchor, newDrag );
			
		} else if ( lineOp ) {
		    Point2D anchor =applyViewPosition( mtk.getAnchor() );
		    Point2D drag =applyViewPosition( mtk.getDrag() != null ? mtk.getDrag() : mtk.getAnchor() );
		    Point2D newDrag =applyViewPosition( e.getPoint() );		    
		    
			mtk.xorLine( container.getGraphics(), anchor, drag );
			mtk.setDrag( e.getPoint() );
			mtk.xorLine( container.getGraphics(), anchor, newDrag );
			
		} else if ( ellipseOp && mtk.getAnchor() != null && mtk.getDrag() != null ) {
		    Point2D anchor =applyViewPosition( mtk.getAnchor() );
		    Point2D drag =applyViewPosition( mtk.getDrag() );
		    Point2D newDrag =applyViewPosition( e.getPoint() );		
		    
			mtk.xorEllipse( container.getGraphics(), anchor, drag );
			mtk.setDrag( e.getPoint() );	
			mtk.xorEllipse( container.getGraphics(), anchor, newDrag );
	
		} else if ( rectangleOp && mtk.getAnchor() != null && mtk.getDrag() != null ) {
			Point2D anchor =applyViewPosition( mtk.getAnchor() );
			Point2D drag =applyViewPosition( mtk.getDrag() );
			Point2D newDrag =applyViewPosition( e.getPoint() );		
	    
			mtk.xorRectangle( container.getGraphics(), anchor, drag );
			mtk.setDrag( e.getPoint() );	
			mtk.xorRectangle( container.getGraphics(), anchor, newDrag );
		
		} else if ( crossOp && mtk.getAnchor() != null && mtk.getDrag() != null ) {
			Point2D anchor =applyViewPosition( mtk.getAnchor() );
			Point2D drag =applyViewPosition( mtk.getDrag() );
			Point2D newDrag =applyViewPosition( e.getPoint() );		
	    
			mtk.xorRectangle( container.getGraphics(), anchor, drag );
			mtk.setDrag( e.getPoint() );	
			mtk.xorRectangle( container.getGraphics(), anchor, newDrag );
			
		} else if ( triangleOp && mtk.getAnchor() != null && mtk.getDrag() != null ) {
			Point2D anchor =applyViewPosition( mtk.getAnchor() );
			Point2D drag =applyViewPosition( mtk.getDrag() );
			Point2D newDrag =applyViewPosition( e.getPoint() );		
	    
			mtk.xorRectangle( container.getGraphics(), anchor, drag );
			mtk.setDrag( e.getPoint() );	
			mtk.xorRectangle( container.getGraphics(), anchor, newDrag );
		}	
				
	}	
	
	private void mouseClickedInsertPolygonPointOp( MouseEvent e ) {	
	    //Point2D pt =mtk.insertPolygonPoint( getScaledPoint( e.getPoint() )  );
		Point2D pt =getScaledPoint( e.getPoint() );
		
		mtk.unselectShapes();
	    List<PolygonShapeUndoInfo> undoInfos =new ArrayList<PolygonShapeUndoInfo>();
		
		boolean firstFound =false;
	    for( MapShape shape : mtk.getShapes() ) {
			
		    if ( shape instanceof PolygonShape ) {		
		    	PolygonShapeUndoInfo undoInfo =new PolygonShapeUndoInfoImpl();
        		undoInfo.cloneInfoFromPolygonShape( (PolygonShape)shape );
        		
		        Point2D matchingPt =((PolygonShape)shape).insertPoint( pt, firstFound );
		        
		        if ( matchingPt != null ) {
		        	undoInfos.add( undoInfo );
		        	
		        	mtk.getSelectedShapes().add( shape );
		            shape.select( true );
		            
		            if ( !firstFound ) {
		                firstFound =true;
		                pt.setLocation( matchingPt.getX(), matchingPt.getY() );
		            }
		        }
		        
		    }
		}
		
	    //setDirty();
	    if ( firstFound ) {
	        setDirty();
	    	undoManager.push( UndoActions.INSERT_POLYGON_POINT, undoInfos );
	    }
	    
	    container.repaint();
	}	
	
	private void mouseClickedDeletePolygonPointOp( MouseEvent e ) {	
	    if ( mtk.anyShapeSelected() ) {

	        Point2D pt =getScaledPoint( e.getPoint() );
			MapShape shape =mtk.getHandleAt( pt );
			
			if ( shape != null && shape instanceof PolygonShape ) {
			    PolygonShape polygon =(PolygonShape)shape;
			    int handleIdx =polygon.getSelectedHandleIndex();
			    Point2D exactPt =polygon.getExactPointAtHandle( handleIdx );
			    
			    List<PolygonShapeUndoInfo> undoInfos =new ArrayList<PolygonShapeUndoInfo>();
			    
			    for( MapShape aShape : mtk.getShapes() ) {
			        if ( aShape instanceof PolygonShape ) {
			        	PolygonShape aPolygonShape =(PolygonShape)aShape;
			        	if ( aPolygonShape.containsEdge( exactPt ) ) {
			        		PolygonShapeUndoInfo undoInfo =new PolygonShapeUndoInfoImpl();
			        		undoInfo.cloneInfoFromPolygonShape( aPolygonShape );

			        		undoInfos.add( undoInfo );
			        		((PolygonShape)aShape).removePointAt( exactPt );
			        	}
			        }
			    }
			    
			    undoManager.push( UndoActions.DELETE_POLYGON_POINT, undoInfos );
			    
			    mtk.getEditedShape().setSelectedHandleIndex( -1 );
			    
			    setDirty();
			    logger.debug( "point deleted !" );
			    container.repaint();
			}
	    }
	}
	
	private void mouseClickedSelectOp( MouseEvent e ) {	
	    
		MapShape graphic =mtk.getShapeAt( getScaledPoint( e.getPoint() ) );
		if ( graphic != null ) { 

			//mtk.captureThematicMapBackground();
			
			if ( !e.isShiftDown() ) {
				mtk.unselectShapes();
			}
			mtk.selectShapeAt( getScaledPoint( e.getPoint() ) );
			
			// double-click
			if ( e.getClickCount() == 2 ) {
				if ( graphic instanceof TitleShape ) {
					JDialogEditMultiLineString dlg =new JDialogEditMultiLineString( 
																JOptionPane.getFrameForComponent( container ), true, 
																"Edit Title", mtk.getThematicMap().getAnnotations().getTitle() );
					dlg.setVisible( true );
					if ( dlg.isOkPressed() ) {
						mtk.getThematicMap().getAnnotations().setTitle( dlg.getInputStr() );
						setDirty();
						container.repaint();
					}
					dlg.dispose();
					
				} else if ( graphic instanceof LegendShape ) {
					JDialogEditMultiLineString dlg =new JDialogEditMultiLineString( JOptionPane.getFrameForComponent( container ), true, 
							"Edit Legend Title", mtk.getThematicMap().getAnnotations().getLegendText() );
					dlg.setVisible( true );
					if ( dlg.isOkPressed() ) {
						mtk.getThematicMap().getAnnotations().setLegendText( dlg.getInputStr() );
						setDirty();
						container.repaint();
					}
					dlg.dispose();					
					
				// Open the JFontChooser dialog when a text shape has been double-clicked
				} else if ( graphic instanceof TextShape && !( graphic instanceof LabelShape )  ) {
				    JDialogFontChooser jFontChooser =new JDialogFontChooser( JOptionPane.getFrameForComponent( container ), true, JDialogFontChooser.Role.TEXT_INPUT );
				    try {
				        TextShape textShape =(TextShape)graphic;
				        
				        jFontChooser.setFontStyle( textShape.getFontStyle() );
				        jFontChooser.setBold( ( textShape.getFontStyle() & Font.BOLD ) > 0 );
				        jFontChooser.setItalic( ( textShape.getFontStyle() & Font.ITALIC ) > 0 );
				        jFontChooser.setFontName( textShape.getFontName() );
				        jFontChooser.setFontSize( textShape.getFontSize() );
				        jFontChooser.setText( textShape.getText() );
				        
				        jFontChooser.applyValuesToUI();
				        jFontChooser.displayText();
				        
				        jFontChooser.setVisible( true );
				        
				        if ( jFontChooser.isOkPressed() ) {				                
			                textShape.setText( jFontChooser.getText() );
			                
			                textShape.setFontName( jFontChooser.getFontName() );
			                textShape.setFontStyle( jFontChooser.getFontStyle() );
			                textShape.setFontSize( jFontChooser.getFontSize() );
			                
			    		    setDirty();     
			                container.repaint();
				        }
		                
		            } catch (Exception e1) {
		                logger.error( e1 );
		            } finally {
		                jFontChooser.dispose();
		            }
		            
		         // Edit polygon information
				 } else if ( graphic instanceof PolygonShape || graphic instanceof LabelShape ) {
				    PolygonShape polygon;
				    if ( graphic instanceof PolygonShape ) {
				    	polygon =(PolygonShape)graphic;
				    } else {
				    	polygon =((PolygonShape)((LabelShape)graphic).getMapShape());
				    }
				    
				    JDialogPolygonProperties dlg 
				    	=new JDialogPolygonProperties( JOptionPane.getFrameForComponent(container), 
				    							    true, polygon, mtk.getShapes() );
				    dlg.setVisible( true );
				    setDirty();
				    
				 // Edit label list information
				 }	else if ( graphic instanceof LabelsListShape ) {
					 JDialogLabelsList dlg =new JDialogLabelsList( JOptionPane.getFrameForComponent(container), true, mtk.getShapes(), (LabelsListShape)graphic );
					 
					 dlg.setVisible( true );
					 
				 } else if ( graphic instanceof CDPointSymbol ) {
					 JDialogCDPointSymbolInfo dlg =new JDialogCDPointSymbolInfo( JOptionPane.getFrameForComponent(container)
							 												   , true
							 												   , mtk.getThematicMap().getData()
							 												   , ((CDPointSymbol)graphic).getPt() );
					 dlg.setVisible( true );
				 }
			}
		 } else if ( !e.isShiftDown() ) {
		     mtk.unselectShapes();
		 }
		
		container.repaint();	    
	}
	
	private void mouseClickedZoomInOp( MouseEvent e ) {
    	setCursor( CursorHelper.Cursors.WAIT );
    	
		if ( SwingUtilities.isRightMouseButton( e ) ) {
			mtk.zoomOut( e.getPoint() );
		} else {
			mtk.zoomIn( e.getPoint() );
		}
	
		displayZoomInfo();
		setPreviousCursor();
	}	
	
	private void mouseClickedZoomOutOp( MouseEvent e ) {
    	setCursor( CursorHelper.Cursors.WAIT );
    	
		if ( SwingUtilities.isRightMouseButton( e ) ) {
			mtk.zoomIn( e.getPoint() );
		} else {
			mtk.zoomOut( e.getPoint() );
		}
		
		displayZoomInfo();
		setPreviousCursor();		
	}		
	
	private void mouseClickedPolygonOp( MouseEvent e ) {
		
		if ( mtk.getAnchor() == null ) {
			mtk.unselectShapes();
			
			container.repaint();
			
			try {
				PolygonShape shape =(PolygonShape)mtk.addShape( MapShapes.POLYGON );
				shape.addPoint( getScaledPoint( e.getPoint() ) );
			    setDirty();
			    
			    undoManager.push( UndoActions.POLYGON, shape );
			} catch (TMapException e1) {
				logger.error( e1 );
			}
			
			mtk.setAnchor( e.getPoint() );
			mtk.setDrag( e.getPoint() );

		} else {
			if ( !SwingUtilities.isRightMouseButton( e ) ) {
				((PolygonShape)mtk.getEditedShape()).addPoint( getScaledPoint( e.getPoint() ) );
				
				mtk.setAnchor( e.getPoint() );
				mtk.setDrag( e.getPoint() );
				
			// Close the polygon on right-click
			} else {
				MapShape polygon =mtk.getEditedShape();
				mtk.closePolygon();
				container.repaint();
				
				mtk.setAnchor( null );
				mtk.setDrag( null );	
				
			    jButtonSelect.doClick();
			    mtk.selectShape( polygon );
				mtk.setEditedShape( polygon );
			}
		    setDirty();
		}

	}
	
	private void mouseClickedLineOp( MouseEvent e ) {
		if ( mtk.getAnchor() == null ) {		
			mtk.unselectShapes();	
			
			container.repaint();					
			
			mtk.setAnchor( e.getPoint() );
			mtk.setDrag( e.getPoint() );		
			
			logger.debug( "clicked anchor=" + mtk.getAnchor() );

		} else {

			if ( !mtk.getAnchor().equals( mtk.getDrag() ) ) {		
				try {
					LineShape lineShape =(LineShape)mtk.addShape( MapShapes.LINE );
									
					lineShape.setP1( getScaledPoint( mtk.getAnchor() ) );
					lineShape.setP2( getScaledPoint( e.getPoint() ) );	
				    setDirty();
				    
				    jButtonSelect.doClick();
				    mtk.selectShape( lineShape );
					mtk.setEditedShape( lineShape );
					
					undoManager.push( UndoActions.LINE, lineShape );
					
				} catch (TMapException e1) {
					logger.error( e1 );
				}
		
				container.repaint();	
			
				mtk.setAnchor( null );
				mtk.setDrag( null );	
			} else {
			    jButtonSelect.doClick();
				mtk.setAnchor( e.getPoint() );
				mtk.setDrag( e.getPoint() );
			}
		}
 
	}
	
	private void mouseClickedRectangleOp( MouseEvent e ) {
		if ( mtk.getAnchor() == null ) {		
			mtk.unselectShapes();	
			
			container.repaint();					
			
			mtk.setAnchor( e.getPoint() );
			mtk.setDrag( e.getPoint() );		
			
			logger.debug( "clicked anchor=" + mtk.getAnchor() );

		} else {
			if ( !mtk.getAnchor().equals( mtk.getDrag() ) ) {		
				try {
					RectangleShape rectangle =(RectangleShape)mtk.addShape( MapShapes.RECTANGLE );

					Rectangle2D rect =new Rectangle2D.Double();
					rect.setFrameFromDiagonal( getScaledPoint( mtk.getAnchor() ), 
  											   getScaledPoint( e.getPoint() ) );	
					
					rectangle.setPt( new Point2D.Double( rect.getMinX(), rect.getMinY() ) );
					rectangle.setWidth( rect.getWidth() );
					rectangle.setHeight( rect.getHeight() );
					
				    jButtonSelect.doClick();
				    mtk.selectShape( rectangle );
					mtk.setEditedShape( rectangle );
					
				    undoManager.push( UndoActions.RECTANGLE, rectangle );
					
				    setDirty();
										
				} catch (TMapException e1) {
					logger.error( e1 );
				}
		
				container.repaint();	
			
				mtk.setAnchor( null );
				mtk.setDrag( null );						
			} else {
			    jButtonSelect.doClick();
				mtk.setAnchor( e.getPoint() );
				mtk.setDrag( e.getPoint() );
			}						
		}	    
	}	
	
	private void mouseClickedCrossOp( MouseEvent e ) {
		if ( mtk.getAnchor() == null ) {		
			mtk.unselectShapes();	
			
			container.repaint();					
			
			mtk.setAnchor( e.getPoint() );
			mtk.setDrag( e.getPoint() );		
			
			logger.debug( "clicked anchor=" + mtk.getAnchor() );

		} else {
			if ( !mtk.getAnchor().equals( mtk.getDrag() ) ) {		
				try {
					CrossShape cross =(CrossShape)mtk.addShape( MapShapes.CROSS );
	
					Rectangle2D rect =new Rectangle2D.Double();
					rect.setFrameFromDiagonal( getScaledPoint( mtk.getAnchor() ), 
											   getScaledPoint( e.getPoint() ) );	
					
					cross.setPt( new Point2D.Double( rect.getMinX(), rect.getMinY() ) );
					cross.setWidth( rect.getWidth() );
					cross.setHeight( rect.getHeight() );
					
				    jButtonSelect.doClick();
				    mtk.selectShape( cross );
					mtk.setEditedShape( cross );
					
				    undoManager.push( UndoActions.CROSS, cross );
					
				    setDirty();
				} catch (TMapException e1) {
					logger.error( e1 );
				}
		
				container.repaint();	
			
				mtk.setAnchor( null );
				mtk.setDrag( null );						
			} else {
			    jButtonSelect.doClick();
				mtk.setAnchor( e.getPoint() );
				mtk.setDrag( e.getPoint() );
			}							
		}	    
	}	
	
	private void mouseClickedTriangleOp( MouseEvent e ) {
		if ( mtk.getAnchor() == null ) {		
			mtk.unselectShapes();	
			
			container.repaint();					
			
			mtk.setAnchor( e.getPoint() );
			mtk.setDrag( e.getPoint() );		
			
			logger.debug( "clicked anchor=" + mtk.getAnchor() );

		} else {
			if ( !mtk.getAnchor().equals( mtk.getDrag() ) ) {		
				try {
					TriangleShape triangle =(TriangleShape)mtk.addShape( MapShapes.TRIANGLE );

					Rectangle2D rect =new Rectangle2D.Double();
					rect.setFrameFromDiagonal( getScaledPoint( mtk.getAnchor() ), 
											   getScaledPoint( e.getPoint() ) );	
					
					triangle.setPt( new Point2D.Double( rect.getMinX(), rect.getMinY() ) );
					triangle.setWidth( rect.getWidth() );
					triangle.setHeight( rect.getHeight() );
					
				    jButtonSelect.doClick();
				    mtk.selectShape( triangle );
					mtk.setEditedShape( triangle );
					
				    undoManager.push( UndoActions.TRIANGLE, triangle );
					
				    setDirty();
										
				} catch (TMapException e1) {
					logger.error( e1 );
				}
		
				container.repaint();	
			
				mtk.setAnchor( null );
				mtk.setDrag( null );						
			} else {
			    jButtonSelect.doClick();
				mtk.setAnchor( e.getPoint() );
				mtk.setDrag( e.getPoint() );
			}							
		}	    
	}		
	
	private void mouseClickedEllipseOp( MouseEvent e ) {
		if ( mtk.getAnchor() == null ) {		
			mtk.unselectShapes();	
			
			container.repaint();					
			
			mtk.setAnchor( e.getPoint() );
			mtk.setDrag( e.getPoint() );		
			
			logger.debug( "clicked anchor=" + mtk.getAnchor() );

		} else {
			if ( !mtk.getAnchor().equals( mtk.getDrag() ) ) {		
				try {
					EllipseShape ellipse =(EllipseShape)mtk.addShape( MapShapes.ELLIPSE );

					Rectangle2D rect =new Rectangle2D.Double();
					rect.setFrameFromDiagonal( getScaledPoint( mtk.getAnchor() ), 
    										   getScaledPoint( e.getPoint() ) );	
					
					ellipse.setPt( new Point2D.Double( rect.getMinX(), rect.getMinY() ) );
					ellipse.setWidth( rect.getWidth() );
					ellipse.setHeight( rect.getHeight() );
					
				    jButtonSelect.doClick();
				    mtk.selectShape( ellipse );
					mtk.setEditedShape( ellipse );	
					
				    undoManager.push( UndoActions.ELLIPSE, ellipse );
					
				    setDirty();
				} catch (TMapException e1) {
					logger.error( e1 );
				}
		
				container.repaint();	
			
				mtk.setAnchor( null );
				mtk.setDrag( null );						
			} else {
			    jButtonSelect.doClick();
				mtk.setAnchor( e.getPoint() );
				mtk.setDrag( e.getPoint() );
			}							
		}	    
	}	
	
	private void mouseClickedFillOp( MouseEvent e ) {
		MapShape shape =mtk.getShapeAt( getScaledPoint( e.getPoint() ) ); 
		if ( shape != null ) {
			undoManager.push( UndoActions.FILL, shape, shape.getFillColor() != null ? new Color( shape.getFillColor().getRed()
																							   , shape.getFillColor().getBlue()
																							   , shape.getFillColor().getGreen()
																							   , shape.getFillColor().getAlpha() )
																					: null );
			
			shape.setFillColor( mtk.getCurrentColor() );
		    setDirty();
			container.repaint();
		} 
		
		jButtonSelect.doClick();
	}	
	
	private void mouseClickedImageOp( MouseEvent e ) {
		File file =miscUIHelper.openBitmapFile();
		
		if ( file != null ) {  
            try {
                ImageShape imageShape =(ImageShape)mtk.addShape( MapShapes.IMAGE );
                
                imageShape.loadFile( file );
                imageShape.setPt( getScaledPoint( e.getPoint() ) );
    		    setDirty();
    		    
    		    jButtonSelect.doClick();
			    mtk.selectShape( imageShape );
				mtk.setEditedShape( imageShape );
				
			    undoManager.push( UndoActions.IMAGE, imageShape );
    		    
    		    container.repaint();
                //logger.debug( "image pt=" + imageShape.getPt() );
                
            } catch (Exception e1) {
                e1.printStackTrace();
            }
		} else {
			jButtonSelect.doClick();
		}
    
	}
	
	private void mouseClickedTextOp( MouseEvent e ) {
		setCursor( CursorHelper.Cursors.WAIT );
	    JDialogFontChooser jFontChooser =new JDialogFontChooser( JOptionPane.getFrameForComponent( container ), true, JDialogFontChooser.Role.TEXT_INPUT );
	    setPreviousCursor();
	    
	    try {    
	        jFontChooser.setVisible( true );
	        
	        if ( jFontChooser.isOkPressed() ) {
                TextShape textShape =(TextShape)mtk.addShape( MapShapes.TEXT );
                
                textShape.setText( jFontChooser.getText() );
                textShape.setPt( getScaledPoint( e.getPoint() ) );
                
                textShape.setFontName( jFontChooser.getFontName() );
                textShape.setFontStyle( jFontChooser.getFontStyle() );
                textShape.setFontSize( jFontChooser.getFontSize() );
                
    		    setDirty();
    		    
			    jButtonSelect.doClick();
			    mtk.selectShape( textShape );
				mtk.setEditedShape( textShape );
				
			    undoManager.push( UndoActions.TEXT, textShape );
				
                container.repaint();
	        } else {
	        	jButtonSelect.doClick();
	        }

        } catch (TMapException e1) {
            logger.error( e1 );
        } finally {
	        jFontChooser.dispose(); 
			mtk.setAnchor( null );
			mtk.setDrag( null );		        
        }	    
	}
	
	private void mouseClickedLabelListOp( MouseEvent e ) {		
		JDialogLabelsList dlg =new JDialogLabelsList( JOptionPane.getFrameForComponent( container ), true, mtk.getShapes(), null );
		
		try {
			dlg.setVisible( true );
			
			if ( dlg.isOkPressed() ) {
				LabelsListShape lls =(LabelsListShape)mtk.addShape( MapShapes.LABELS_LIST );
				lls.setPt( getScaledPoint( e.getPoint() ) );
				lls.setName( dlg.getJTextFieldLabelListName().getText() );
				lls.setStyle( dlg.getStyle() );
				lls.setBackgroundTransparent( dlg.getJCheckBoxTransparentBackground().isSelected() );
				lls.setVisible( dlg.getJCheckBoxVisible().isSelected() );
				
    		    setDirty();
    		    
			    jButtonSelect.doClick();
			    mtk.selectShape( lls );
				mtk.setEditedShape( lls );
				
			    undoManager.push( UndoActions.LABELS_LIST, lls );
    		    
                container.repaint();
			} else {
				jButtonSelect.doClick();
			}
		} catch( Exception e2 ) {
			logger.error( e2 );
		} finally {
			dlg.dispose();			
		}
   
	}	
	
	/**
	 * Convert a Point2D to real coordinates based on zoom factor
	 * @param pt2d
	 * @return
	 */
	private Point2D getScaledPoint( Point2D pt2d ) {
	    return mtk.convertScreenToMap( pt2d );
	}
		
    /**
     * @param container The container to set.
     */
    public void setContainer(Container container) {
        this.container = container;
    }
    
    public void setDataTableModel( TableModel dataTableModel ) {
    	this.dataTableModel =dataTableModel;
    }
    
    /**
     * @param mtk The mtk to set.
     */
    public void setMtk(MappingToolKit mtk) {
        this.mtk = mtk;
    }
    
    /**
     * 
     * @param acquisitionOp setting acquisition state
     */
    public void setDigitizerWithoutDescOp( boolean digitizerWithoutDescOp ){
    	this.digitizerWithoutDescOp = digitizerWithoutDescOp;
    	
    	if ( digitizerWithoutDescOp ) {
    		if ( startDigitizer() ) {
    			//((DigitizerServiceImpl)digitizerService).addObserver(this);
    		} else {
    			jButtonSelect.doClick();
    		}
    	} else {
    		digitizerService.stop();
    	}
    }
    
    public void setDigitizerOp( boolean digitizerOp ){
    	this.digitizerOp = digitizerOp;
    	
    	if ( digitizerOp ) {
    		if ( startDigitizer() ) {
    			//((DigitizerServiceImpl)digitizerService).addObserver(this);
    		} else {
    			jButtonSelect.doClick();
    		}
    	} else {
    		digitizerService.stop();
    	}
    }    
    
    
    
    /**
     * @param fillOp The fillOp to set.
     */
    public void setFillOp(boolean fillOp) {
        this.fillOp = fillOp;
        
        if ( fillOp ) {
        	setCursor( CursorHelper.Cursors.FILL );
        }
    }
    
    /**
     * @param lineOp The lineOp to set.
     */
    public void setLineOp(boolean lineOp) {
        this.lineOp = lineOp;
        
        if ( lineOp ) {
        	setCursor( CursorHelper.Cursors.LINE );
        }
    }    
    
    public void setCrossOp(boolean crossOp) {
		this.crossOp = crossOp;

        if ( crossOp ) {
        	setCursor( CursorHelper.Cursors.DEFAULT );
        }   
	}

	public void setEllipseOp(boolean ellipseOp) {
		this.ellipseOp = ellipseOp;
		
        if ( ellipseOp ) {
        	setCursor( CursorHelper.Cursors.DEFAULT );
        }   		
	}

	public void setRectangleOp(boolean rectangleOp) {
		this.rectangleOp = rectangleOp;
		
        if ( rectangleOp ) {
        	setCursor( CursorHelper.Cursors.DEFAULT );
        }   		
	}

	public void setTriangleOp(boolean triangleOp) {
		this.triangleOp = triangleOp;
		
        if ( triangleOp ) {
        	setCursor( CursorHelper.Cursors.DEFAULT );
        }   		
	}	

	/**
     * @param imageOp The imageOp to set.
     */
    public void setImageOp(boolean imageOp) {
        this.imageOp = imageOp;
        
        if ( imageOp ) {
        	setCursor( CursorHelper.Cursors.IMAGE );
        }
    }    
    
    /**
     * @param moveOp The moveOp to set.
     */
    public void setMoveOp(boolean moveOp) {
        this.moveOp = moveOp;
        
        if ( moveOp ) {
        	setCursor( CursorHelper.Cursors.MOVE );
        }        
     }
    
    /**
     * @param multiSelectOp The multiSelectOp to set.
     */
    public void setMultiSelectOp(boolean multiSelectOp) {
        this.multiSelectOp = multiSelectOp;
        
        if ( multiSelectOp ) {
        	setCursor( CursorHelper.Cursors.DEFAULT );
        }        
    }
    
    /**
     * @param polygonOp The polygonOp to set.
     */
    public void setPolygonOp(boolean polygonOp) {
        this.polygonOp = polygonOp;
        
        if ( polygonOp ) {
        	setCursor( CursorHelper.Cursors.DEFAULT );
        }
    }
    
    /**
     * @param splitPolygonOp The splitPolygonOp to set.
     */
    public void setSplitPolygonOp(boolean splitPolygonOp) {
        this.splitPolygonOp = splitPolygonOp;
        
        if ( splitPolygonOp ) {
        	setCursor( CursorHelper.Cursors.SPLIT );
        }
    }
    
    /**
     * 
     */
    public void setInsertPolygonPointOp( boolean insertPolygonPointOp ) {
        this.insertPolygonPointOp =insertPolygonPointOp;
        
        if ( insertPolygonPointOp ) {
        	setCursor( CursorHelper.Cursors.ADD_POINT );
        }
    }
    
    /**
     * 
     */
    public void setDeletePolygonPointOp( boolean deletePolygonPointOp ) {
        this.deletePolygonPointOp =deletePolygonPointOp;
        
        if ( deletePolygonPointOp ) {
        	setCursor( CursorHelper.Cursors.DEL_POINT );        	
        }
    }
    
    public void setDisputedBorderOp( boolean disputedBorderOp ) {
    	this.disputedBorderOp =disputedBorderOp;
    	
        if ( disputedBorderOp ) {
        	setCursor( CursorHelper.Cursors.DEFAULT );
        }    	
    }
    
    /**
     * @param useExistingBoundariesOp The UseExistingBoundariesOp to set.
     */
    public void setUseExistingBoundariesOp(boolean useExistingBoundariesOp) {
        this.useExistingBoundariesOp = useExistingBoundariesOp;
        
        if ( mtk.getAnchor() != null ) {
	        // To remove the edition "moving line"
	        mtk.setDrag( (Point2D)mtk.getAnchor().clone() );
        }
        
        if ( useExistingBoundariesOp ) {
        	setCursor( CursorHelper.Cursors.DEFAULT );
        }
        
        container.repaint();
    }
    
    /**
     * @param selectOp The selectOp to set.
     */
    public void setSelectOp(boolean selectOp) {  	
        this.selectOp = selectOp;
        
        if ( selectOp ) {
        	setCursor( CursorHelper.Cursors.DEFAULT );
        }
    }
    
    /**
     * @param textOp The textOp to set.
     */
    public void setTextOp(boolean textOp) {
        this.textOp = textOp;
        
        if ( textOp ) {
        	setCursor( CursorHelper.Cursors.TEXT );
        }
    }   
    
    /**
     * @param labelListOp The labelListOp to set.
     */
    public void setLabelListOp(boolean labelListOp) {
        this.labelListOp = labelListOp;
        
        if ( labelListOp ) {
        	setCursor( CursorHelper.Cursors.LABELS_LIST );
        }
    }     
    
    public void setZoomInOp( boolean zoomInOp ) {
    	this.zoomInOp =zoomInOp;
    	if ( zoomInOp ) {
        	setCursor( CursorHelper.Cursors.ZOOM_IN );    		
    	}
    }
    
    public void setZoomOutOp( boolean zoomOutOp ) {
    	this.zoomOutOp =zoomOutOp;
    	if ( zoomOutOp ) {
        	setCursor( CursorHelper.Cursors.ZOOM_OUT );    		
    	}
    }   
    
    private void displayZoomInfo() {
    	jLabelZoomInfo.setText( mtk.getZoomFactor() + "%" );
    }
    
    private void setPointEntryXYColumns() {
    	try {
			pointEntryXColumnIdx =attrHelper.getIdxForAttrName( mtk.getThematicMap().getData().getHeader() , pointEntryXColumn );
		} catch (TMapException e) {
			pointEntryXColumnIdx =-1;
		}    
    	try {
			pointEntryYColumnIdx =attrHelper.getIdxForAttrName( mtk.getThematicMap().getData().getHeader() , pointEntryYColumn );
		} catch (TMapException e) {
			pointEntryYColumnIdx =-1;
		}      		
		
		mtk.getThematicMap().getData().setXColumnIdx( pointEntryXColumnIdx );
		mtk.getThematicMap().getData().setYColumnIdx( pointEntryYColumnIdx );    		
		
    	setCursor( CursorHelper.Cursors.DEFAULT );       	
    }
    
    public void setPointEntryWithoutDescOp( boolean pointEntryWithoutDescOp ) {
    	this.pointEntryWithoutDescOp =pointEntryWithoutDescOp;
    	if ( pointEntryWithoutDescOp ) {
    		setPointEntryXYColumns();
    	}
    }
    
    public void setPointEntryOp( boolean pointEntryOp ) {    	
    	this.pointEntryOp =pointEntryOp;
    	if ( pointEntryOp ) {
    		setPointEntryXYColumns();
    	}
    }    
    
    public void setPointEntryXColumn( String pointEntryXColumn ) {
    	this.pointEntryXColumn =pointEntryXColumn;
    }
    
    public void setPointEntryYColumn( String pointEntryYColumn ) {
    	this.pointEntryYColumn =pointEntryYColumn;
    }    

	/**
     * @param buttonPolygon The jButtonPolygon to set.
     */
    public void setJButtonPolygon(JToggleButton buttonPolygon) {
        this.jButtonPolygon = buttonPolygon;
    }  
    
    /**
     * @param buttonSelect The jButtonSelect to set.
     */
    public void setJButtonSelect(JToggleButton buttonSelect) {
        jButtonSelect = buttonSelect;
    }
    
    public void setJPanelCDPoint( JPanelCDPoint jPanelCDPoint ) {
    	this.jPanelCDPoint =jPanelCDPoint;
    }
    
    /**
     * Find another way later
     * @param jLabelCoords
     */
	public void setJLabelCoords( JLabel jLabelCoords ) {
		this.jLabelCoords =jLabelCoords;
	}
	
    /**
     * Find another way later
     * @param jLabelZoomInfo
     */
	public void setJLabelZoomInfo( JLabel jLabelZoomInfo ) {
		this.jLabelZoomInfo =jLabelZoomInfo;
	}	

	/*public void registerObserver(SimpleObserver observer) {
		observedImpl.registerObserver( observer );
	}

	public void unregisterObserver(SimpleObserver observer) {
		observedImpl.unregisterObserver( observer );
	}
	
	public void notifyObservers( Object p ) {
		observedImpl.notifyObservers( p );
	}*/
	
	private void setCursor( CursorHelper.Cursors cursor ) {
		cursorHelper.setCursor( container, cursor );
	}
	
	private void setPreviousCursor() {
		cursorHelper.setPreviousCursor( container );
	}

	/*public void update(Observable observable, Object obj) {
		processDigitizerEntry( (DigitizerEntry)obj );
	}*/
	
	@EventSubscriber(eventClass=DigitizerInput.class)
    public void onEvent(DigitizerInput evt) {
		processDigitizerEntry( evt.getDigitizerEntry() );
	}

    
}
